import axios from "axios"
import { sessionCache } from "@/utils/cache.js"
import store from "@/store/index.js"
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
import { blobValidate, tansParams } from "@/utils/index.js"
import { saveAs } from 'file-saver'

const allApis = {}
const env = import.meta.env

// 自动扫描子模块api并导入
const apisContext = import.meta.glob("@/apis/**/*.js", {eager: true})
Object.keys(apisContext).forEach((path) => {
    const apiModule = apisContext[path]
    // 兼容 import export 和 require module.export两种规范
    Object.assign(allApis, apiModule.default || apiModule)
})
const instance = axios.create({
    // axios中请求配置有baseURL选项，表示请求URL公共部分
    baseURL: env.VITE_APP_BASE_API,
    // 超时
    timeout: 10000,
})

const requests = {} // 包裹请求方法的容器

// 请求格式、参数的统一
for (const key in allApis) {
    const api = allApis[key] // api中有请求方式method以及请求路径url
    requests[key] = async function (options) {
        // 默认参数
        options = Object.assign(
            {
                params: undefined, // 请求参数
                jointUrl: "", // restful请求格式中添加/{id}
                needToken: true, // 是否需要token，true/false
                canRepeatSubmit: false, // 是否可以重复提交
                loading: false,
                loadingText: "正在加载数据，请稍候...",
                type: "form",
            },
            options
        )

        // 不同请求方法的判断
        let response = {} // 请求的返回值
        // 配置参数
        const config = {}
        config.options = options
        // 打印日志
        if (env.DEV) {
            if (!options.params) {
                console.log('%c请求地址：' + api.url, 'color:#C41D7F')
            } else {
                if (JSON.stringify(options.params).length < 90) {
                    console.log(`%c请求地址：${ api.url }  %c请求参数：${ JSON.stringify(options.params) }`, 'color:#C41D7F', '')
                } else {
                    console.groupCollapsed(`%c请求地址：${ api.url }`, 'color:#C41D7F;font-weight:normal')
                    console.debug('请求参数:', options.params)
                    console.groupEnd()
                }
            }
        }
        // 不可重复提交
        if (
            api.method === "post" ||
            api.method === "put" ||
            api.method === "patch"
        ) {
            if (!config.options.canRepeatSubmit) {
                const requestObj = {
                    url: api.url,
                    data:
                        typeof options.params === "object"
                            ? JSON.stringify(options.params)
                            : options.params,
                    time: new Date().getTime(),
                }
                const sessionObj = sessionCache.getJSON("preRequestCache")
                // 缓存请求
                if (
                    sessionObj === undefined ||
                    sessionObj === null ||
                    sessionObj === ""
                ) {
                    sessionCache.setJSON("preRequestCache", requestObj)
                } else {
                    const s_url = sessionObj.url // 请求地址
                    const s_data = sessionObj.data // 请求数据
                    const s_time = sessionObj.time // 请求时间
                    const interval = 500 // 间隔时间(ms)，小于此时间视为重复提交

                    if (
                        s_data === requestObj.data &&
                        requestObj.time - s_time < interval &&
                        s_url === requestObj.url
                    ) {
                        const message = "数据正在处理，请勿重复提交"
                        console.warn(`[${ s_url }]: ` + message)
                        return Promise.reject(new Error(message))
                    } else {
                        sessionCache.setJSON("preRequestCache", requestObj)
                    }
                }
            }

            // axios默认是用application/json
            let newParams = options.params
            // 我们默认转为form-data
            if (options.type === "form") {
                newParams = new FormData()
                for (const key in options.params) {
                    if (
                        options.params[key] !== undefined &&
                        options.params[key] !== null
                    ) {
                        newParams.append(key, options.params[key])
                    }
                }
            }
            try {
                response = await instance[api.method](
                    api.url + options.jointUrl,
                    newParams,
                    config
                )
            } catch (err) {
                console.warn(`[${ api.url }]: ` + JSON.stringify(err))
                return Promise.reject(err)
            }
        } else {
            config.params = options.params
            try {
                response = await instance[api.method](
                    api.url + options.jointUrl,
                    config
                )
            } catch (err) {
                console.warn(`[${ api.url }]: ` + JSON.stringify(err))
                return Promise.reject(err)
            }
        }
        return response
    }
}

let loading
// 是否显示重新登录
let isReLoginShow
// 是否有刷新token，若有刷新token，返回码为402为token过期，则重新请求，传refreshToken请求token
const hasRefreshToken = true
const refreshTokenUrl = "/auth/refreshToken"

// 拦截器的添加
instance.interceptors.request.use(
    (config) => {
        // 发起请求前
        if (config.options.loading) {
            loading = ElLoading.service({text: config.options.loadingText, background: "rgba(0, 0, 0, 0.7)"})
        }

        if (config.options.needToken) {
            // 登录流程控制中，根据本地是否存在token判断用户的登录情况
            // 但是即使token存在，也有可能token是过期的，所以在每次的请求头中携带token
            // 后台根据携带的token判断用户的登录情况，并返回给我们对应的状态码
            // 而后我们可以在响应拦截器中，根据状态码进行一些统一的操作。
            const token = store.user.token
            token && (config.headers[store.user.tokenName ? store.user.tokenName : 'token'] = token)
        }

        return config
    },
    (config) => {
        if (config.options.loading) loading.close()
        console.warn(`接口[${ config.url }]: ` + "请求错误，请求稍后重试")
        ElMessage.error('请求错误，请求稍后重试')
    }
)

instance.interceptors.response.use(
    (response) => {
        if (response.config.options.loading) loading.close()
        // 二进制数据则直接返回
        if (
            response.request.responseType === "blob" ||
            response.request.responseType === "arraybuffer"
        ) {
            return response.data
        }

        if (env.DEV) {
            if (response.status === 200) {
                if (!response.data.data) {
                    console.log('%c请求：' + response.config.url + '，返回结果：' + response.data.msg, 'color:#2B9939')
                } else {
                    console.groupCollapsed('%c请求：' + response.config.url + '，返回结果：' + response.data.msg, 'color:#2B9939;font-weight:normal')
                    const data = response.data.data
                    Object.keys(data).forEach(key => {console.log(`%c${ key }:`, 'color:#792675;font-weight:bold', data[key])})
                    console.groupEnd()
                }
            }
        }
        if (response.data.code === 200) {
            return Promise.resolve(response.data) // 后面要用到的就只有data
        } else if (response.data.code === 401 || response.data.code === 402) {
            // 如果token过期，并且有刷新token
            if (response.data.code === 402 && hasRefreshToken) {
                console.log("token过期")
                const refreshToken = store.user.refreshToken
                if (refreshToken && refreshToken !== "") {
                    console.log("刷新token")

                    // 请求刷新token，添加请求参数
                    const formData = new FormData()
                    formData.append("grant_type", "refresh_token")
                    formData.append("refreshToken", refreshToken)

                    return axios
                        .post(response.config.baseURL + refreshTokenUrl, formData, {
                            headers: {
                                "Content-Type":
                                    "application/x-www-form-urlencoded;charset=utf-8",
                                Authorization: refreshToken,
                            },
                        })
                        .then((res) => {
                            // 刷新token成功了，res.token不为空
                            if (res.data.token) {
                                // 重置token
                                store.user.setTokenName(res.data.tokenName)
                                store.user.setToken(res.data.token)
                                // 修改新的token
                                response.config.headers[res.data.tokenName] = res.data.token
                                // 刷新token后，重新请求
                                // 用原先的请求config，只是修改了token再次访问下，实现用户无感的刷新token再次请求数据
                                console.log("刷新成功后，重新请求")
                                return axios
                                    .request(response.config)
                                    .then((res2) => {
                                        if (res2.data.code === 200) {
                                            return Promise.resolve(res2.data) // 后面要用到的就只有data
                                        } else {
                                            return Promise.reject(res2.data.msg)
                                        }
                                    })
                                    .catch((err2) => {
                                        return Promise.reject(err2)
                                    })
                            }
                        })
                        .catch((err1) => {
                            // 若没有刷新成功
                            console.warn("刷新token失败")
                        })
                }
            }
            if (!isReLoginShow) {
                isReLoginShow = true
                ElMessageBox
                    .confirm(
                        response.data.msg ? response.data.msg : "登录状态已过期，您可以继续留在该页面，或者重新登录",
                        "系统提示",
                        {
                            confirmButtonText: "重新登录",
                            cancelButtonText: "取消",
                            type: "warning",
                        }
                    )
                    .then(() => {
                        isReLoginShow = false
                        store.user.fedLogOut().then(() => {
                            // 如果是登录页面不需要重新加载
                            if (window.location.hash.indexOf("#/login") !== 0) {
                                location.href = "/"
                            }
                        })
                    })
                    .catch(() => {
                        isReLoginShow = false
                    })
            }
            return Promise.reject(response.data.msg)
        } else {
            console.warn(response.data.msg)
            ElMessage.error(response.data.msg)
            return Promise.reject(response.data)
        }
    },
    (err) => {
        console.warn(err)
        // 发起请求前
        if (err.response.config.options.loading) loading.close()
        if (err && err.response) {
            switch (err.response.status) {
                case 400:
                    err.message = "请求错误(400)"
                    break
                case 401:
                    err.message = "未授权，请重新登录(401)"
                    break
                case 403:
                    err.message = "拒绝访问(403)"
                    break
                case 404:
                    err.message = "请求出错(404)"
                    break
                case 408:
                    err.message = "请求超时(408)"
                    break
                case 500:
                    err.message = "服务器错误(500)"
                    break
                case 501:
                    err.message = "服务未实现(501)"
                    break
                case 502:
                    err.message = "网络错误(502)"
                    break
                case 503:
                    err.message = "服务不可用(503)"
                    break
                case 504:
                    err.message = "网络超时(504)"
                    break
                case 505:
                    err.message = "HTTP版本不受支持(505)"
                    break
                default:
                    err.message = `连接出错(${ err.response.status })!`
            }
        } else {
            err.message = "连接服务器失败!"
        }
        ElMessage.error(err.message)
        return Promise.reject(err.message)
    }
)

// 通用下载方法
let downloadLoadingInstance
requests['download'] = function download(url, params, filename, config) {
    downloadLoadingInstance = ElLoading.service({text: "正在下载数据，请稍候", background: "rgba(0, 0, 0, 0.7)"})
    return instance.post(url, params, {
        options: {needToken: true},
        transformRequest: [(params) => { return tansParams(params) }],
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        responseType: 'blob'
    }).then(async (data) => {
        const isBlob = blobValidate(data)
        if (isBlob) {
            const blob = new Blob([data])
            saveAs(blob, filename)
        } else {
            const resText = await data.text()
            const rspObj = JSON.parse(resText)
            const errMsg = rspObj.msg || '系统未知错误，请反馈给管理员'
            ElMessage.error(errMsg)
        }
        downloadLoadingInstance.close()
    }).catch((r) => {
        console.error(r)
        ElMessage.error('下载文件出现错误，请联系管理员！')
        downloadLoadingInstance.close()
    })
}

requests['test'] = function test() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({code: 200, msg: "测试成功", data: {}})
        }, 1000)
    })
}


export default requests
